package com.tax.manager;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author 段德辉
 */
@Component("redisApiManager")
@Configuration
public class RedisApiManager {
    private static Logger logger = LoggerFactory.getLogger(RedisApiManager.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    public Set<String> keys(String pattern) {
        return stringRedisTemplate.keys(pattern);
    }

    public String get(String key) {
        try {
            return stringRedisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            logger.warn("获取对象缓存key：{},异常：", key, e);
        }
        return null;
    }


    public boolean set(String key, String value) {
        try {
            stringRedisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            logger.warn("存储对象缓存key：{},异常：", key, e);
            return false;
        }
    }


    public boolean set(String key, String t, long timeout, TimeUnit unit) {
        try {
            stringRedisTemplate.opsForValue().set(key, t, timeout, unit);
            return true;
        } catch (Exception e) {
            logger.warn("设置缓存key：{},加入缓存异常：", key, e);
            return false;
        }
    }

    public <T> T get(String key, Class<T> clazz) {
        try {
            String tJson = stringRedisTemplate.opsForValue().get(key);
            if (tJson != null) {
                return JSON.parseObject(tJson, clazz);
            }
        } catch (Exception e) {
            logger.warn("获取对象缓存key：{},异常：", key, e);
        }
        return null;
    }


    public <T> boolean set(String key, T t, long timeout, TimeUnit unit) {
        String tJson = "";
        try {
            tJson = JSON.toJSONString(t);
            stringRedisTemplate.opsForValue().set(key, tJson, timeout, unit);
            return true;
        } catch (Exception e) {
            logger.warn("设置缓存key：{},value：{},加入缓存异常：", key, tJson, e);
            return false;
        }
    }

    public <T> T get(String key, Class<T> clazz, long timeout, TimeUnit unit) {
        try {
            String tJson = stringRedisTemplate.opsForValue().get(key);
            if (tJson != null) {
                T t = JSON.parseObject(tJson, clazz);
                if (timeout > 0) {
                    stringRedisTemplate.expire(key, timeout, unit);
                }
                return t;
            }
        } catch (Exception e) {
            logger.warn("获取对象缓存key：{},异常：", key, e);
        }
        return null;
    }

    public <T> List<T> list(String key, Class<T> clazz) {
        try {
            String tJson = stringRedisTemplate.opsForValue().get(key);
            if (tJson != null) {
                return JSON.parseArray(tJson, clazz);
            }
        } catch (Exception e) {
            logger.warn("获取对象缓存key：{},异常：", key, e);
        }
        return new ArrayList<>();
    }

    public <T> List<T> mget(String prefix, List<String> keys, Class<T> clazz) {
        List<T> t = new ArrayList<>();
        try {
            List<String> listPrefixKey = new ArrayList<>();
            for (String key : keys) {
                listPrefixKey.add(prefix + key);
            }
            List<String> tJsonList = stringRedisTemplate.opsForValue().multiGet(listPrefixKey);
            if (tJsonList != null && !tJsonList.isEmpty()) {
                for (String tJson : tJsonList) {
                    t.add(JSON.parseObject(tJson, clazz));
                }
            }
        } catch (Exception e) {
            logger.warn("批量获取对象缓存key：{},异常：", keys, e);
        }
        return t;
    }

    public <T> void mset(Map<String, T> tMap, long timeout, TimeUnit unit) {
        try {
            Map<String, String> map = new HashMap<>(16);
            for (Map.Entry<String, T> entry : tMap.entrySet()) {
                map.put(entry.getKey(), JSON.toJSONString(entry.getValue()));
            }
            stringRedisTemplate.opsForValue().multiSet(map);
            for (Map.Entry<String, String> entry : map.entrySet()) {
                stringRedisTemplate.expire(entry.getKey(), timeout, unit);
            }
        } catch (Exception e) {
            logger.warn("批量存储对象缓存key：{},异常：", tMap.keySet(), e);
        }
    }

    public boolean remove(String key) {
        try {
            stringRedisTemplate.delete(key);
        } catch (Exception e) {
            logger.warn("移除缓存key：{},异常：", key, e);
        }
        return true;
    }

    public boolean setnx(String key, long sysTime, long timeout, TimeUnit unit) {
        boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key, String.valueOf(sysTime));
        stringRedisTemplate.expire(key, timeout, unit);
        return result;
    }

    public boolean isExist(String key, long sysTime, long timeout, TimeUnit unit) {
        boolean flag = setnx(key, System.currentTimeMillis() + sysTime, timeout, unit);
        if (!flag) {
            String time = get(key, String.class);
            if (System.currentTimeMillis() > Long.valueOf(time)) {
                remove(key);
                return setnx(key, System.currentTimeMillis() + sysTime, timeout, unit);
            } else {
                return false;
            }
        }
        return true;
    }

    public long increment(String key, long timeout, TimeUnit unit) {
        try {
            long i = stringRedisTemplate.opsForValue().increment(key, 1);
            if (i == 1) {
                stringRedisTemplate.expire(key, timeout, unit);
            }
            return i;
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 有序集合-增加元素
     *
     * @param key    key
     * @param score  score
     * @param number number
     */
    public Boolean zadd(String key, Double score, String number) {
        try {
            return stringRedisTemplate.opsForZSet().add(key, number, score);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 有序集合-查看元素位置
     *
     * @param key    key
     * @param number number
     */
    public Long zrank(String key, String number) {
        try {
            return stringRedisTemplate.opsForZSet().rank(key, number);
        } catch (Exception e) {
            return -1L;
        }
    }

    /**
     * 有序集合-移除元素
     *
     * @param key    key
     * @param number numbers
     */
    public Long zrem(String key, Object... number) {
        try {
            return stringRedisTemplate.opsForZSet().remove(key, number);
        } catch (Exception e) {
            return -1L;
        }
    }

    /**
     * 有序集合-返回集合中排名start stop之前的成员
     *
     * @param key   key
     * @param start start
     * @param stop  stop
     */
    public Set<ZSetOperations.TypedTuple<String>> zrange(String key, Long start, Long stop) {
        try {
            return stringRedisTemplate.opsForZSet().rangeWithScores(key, start, stop);
        } catch (Exception e) {
            return new HashSet<>();
        }
    }

    /**
     * 有序集合-删除集合中分值介于start stop之前的成员
     *
     * @param key   key
     * @param start start
     * @param stop  stop
     */
    public Long zremrangebyscore(String key, Long start, Long stop) {
        try {
            return stringRedisTemplate.opsForZSet().removeRangeByScore(key, start, stop);
        } catch (Exception e) {
            return -1L;
        }
    }

    public Long zcard(String key) {
        try {
            return stringRedisTemplate.opsForZSet().zCard(key);
        } catch (Exception e) {
            return -1L;
        }
    }

    /**
     * 哈希 添加
     *
     * @param key     key
     * @param hashKey hashKey
     * @param value   value
     */
    public void hSet(String key, Object hashKey, Object value, Long timeOut) {
        HashOperations<String, Object, Object> hash = stringRedisTemplate.opsForHash();
        hash.put(key, hashKey, value);
        if(timeOut !=null &&timeOut>0) {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
    }

    /**
     * 哈希 添加
     *
     * @param key key
     * @param map map
     */
    public void hmSet(String key, Map<Object, Object> map, Long timeOut) {
        HashOperations hash = redisTemplate.opsForHash();
        hash.putAll(key, map);
        if(timeOut !=null &&timeOut>0) {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
    }

    /**
     * 哈希获取数据
     *
     * @param key     key
     * @param hashKey hashKey
     * @return Object
     */
    public Object hGet(String key, Object hashKey, Long timeOut) {
        HashOperations hash = redisTemplate.opsForHash();
        Object object = hash.get(key, hashKey);
        if(timeOut !=null &&timeOut>0) {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
        return object;
    }

    /**
     * 哈希获取数据
     *
     * @param key      key
     * @param hashKeys hashKey
     * @return Object
     */
    public List<Object> hmGet(String key, List<Object> hashKeys, Long timeOut) {
        HashOperations hash = redisTemplate.opsForHash();
        List<Object> list = hash.multiGet(key, hashKeys);
        if(timeOut !=null &&timeOut>0) {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
        return list;
    }

    public List<Object> hmGetStr(String key, List<Object> hashKeys, Long timeOut) {
        HashOperations hash = stringRedisTemplate.opsForHash();
        List<Object> list = hash.multiGet(key, hashKeys);
        if(timeOut !=null &&timeOut>0) {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
        return list;
    }

    /**
     * 哈希删除数据
     *
     * @param key      key
     * @param hashKeys hashKeys
     * @return Long
     */
    public Long hDel(String key, Object... hashKeys) {
        HashOperations hash = redisTemplate.opsForHash();
        return hash.delete(key, hashKeys);
    }

    public Set sMember(String key, Long timeOut) {
        Set set = redisTemplate.opsForSet().members(key);
        if(timeOut !=null &&timeOut>0) {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
        return set;
    }

    public Long sAdd(String key, Long timeOut, Object... items) {
        Long result = redisTemplate.opsForSet().add(key, items);
        if(timeOut !=null && timeOut>0) {
            redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
        return result;
    }

    public Long scard(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    public boolean sisMember(String key, String value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    public Long sRem(String key, Object... objects) {
        return stringRedisTemplate.opsForSet().remove(key, objects);
    }


    public Long ttl(String key) {
        try {
            return stringRedisTemplate.getExpire(key);
        } catch (Exception e) {
            logger.error(ExceptionUtils.getStackTrace(e));
            return -1L;
        }
    }

    public void selectDd(int dbIndex) {
        JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) stringRedisTemplate.getConnectionFactory();
        jedisConnectionFactory.setDatabase(dbIndex);
        stringRedisTemplate.setConnectionFactory(jedisConnectionFactory);
    }

    public GeoResults geoRadius(String key, Double lat, Double lng, Double radius) {
        Circle circle = new Circle(new Point(lng, lat), new Distance(radius));
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        args.includeCoordinates();
        args.includeDistance();
        return stringRedisTemplate.opsForGeo().geoRadius(key, circle, args);
    }

    public void expire(String key, int timeout, TimeUnit timeUnit) {
        try {
            stringRedisTemplate.expire(key,timeout,timeUnit);
        } catch (Exception e) {
            logger.error(ExceptionUtils.getStackTrace(e));
        }
    }
}
